/*
 * Authenticate
 * 
 * Version information
 * 
 * Feb 17, 2006
 * 
 * Copyright (c) AmSoft Systems, 2006
 */
package net.amsoft.iservice.isso.webapp.struts.action;

import static net.amsoft.iservice.isso.util.ISSOConstants.SERV_ID_COOKIE_NAME;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.amsoft.iservice.isso.client.ISSOClient;
import net.amsoft.iservice.isso.client.SAMLDataObject;
import net.amsoft.iservice.isso.service.ISSOService;
import net.amsoft.iservice.isso.webapp.dataObject.LoginData;
import net.amsoft.iservice.isso.webapp.dataObject.SPData;
import net.amsoft.iservice.isso.webapp.struts.form.LoginForm;
import net.amsoft.iservice.isso.util.ISSOConstants;
import net.amsoft.iservice.isso.util.SAMLProcessor;
import net.amsoft.iservice.util.IServiceUtil;
import net.amsoft.iservice.util.ResolverClient;
import net.amsoft.iservice.util.exception.IServiceException;

import org.apache.log4j.Logger;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;

/**
 * This will receive the user's password and do the authentication part, and
 * then return the Artifact to the SP
 * 
 * @author rahul
 * 
 */
public class Authenticate extends BaseAction {
	private static Logger oLogger = Logger.getLogger(Authenticate.class);

    public ActionForward process(ActionMapping oMapping,
            ActionForm oActionForm, HttpServletRequest oRequest,
            HttpServletResponse oResponse) throws Exception {

        oLogger.info("process : entry");
        // get the session objects
        LoginData oLoginData = (LoginData) oRequest.getSession(false)
                .getAttribute(ISSOConstants.SESSION_LOGIN_OBJ);
        HashMap ohSPData = (HashMap) oRequest.getSession(false).getAttribute(
                ISSOConstants.SESSION_LOGIN_HASH_OBJ);

        // LoginForm containing password
        LoginForm oLoginForm = (LoginForm) oActionForm;
        ActionForward oActionForward = null;
        ActionMessages oMessages = new ActionErrors();

        String sSamePageCookie = getSamePageCookie(oRequest);
        oLogger.info("process() : Service cookie="+ sSamePageCookie);

        String sSpXri = null;

        if (sSamePageCookie != null) {
            sSpXri = sSamePageCookie;

            // Delete same page cookie
            Cookie oCookie = new Cookie(SERV_ID_COOKIE_NAME, null);
            oCookie.setSecure(true);
            oCookie.setMaxAge(0);
            oResponse.addCookie(oCookie);
        } else
            sSpXri = oLoginForm.getSpXri();

        SPData oSPData = (SPData) ohSPData.get(sSpXri);

        oLogger.info("process() : SpXri=" + sSpXri);
        if (oSPData == null) {
            oMessages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
                    ISSOConstants.ERR_INVALID_REQUEST));
            saveErrors(oRequest, oMessages);
            oLogger.info("process() : Message : Service could not be identified");
            return oMapping.findForward(ISSOConstants.INAME_FWD);
        }

        SAMLDataObject oAuthnRequestSAMLData = oSPData.getSAMLData();
        // prepare artifact response document
        String sArtifact = createArtifact(ResolverClient.getFinalMetaData(
                oAuthnRequestSAMLData.getSubjectNameIDNameQualifier())
                .getProviderID());
        try {
            if (oLoginData.getSessionCookie() == null
                    || !verifyCookieState(oSPData.getGlobalInumber(),
                            oLoginData.getSessionCookie(), true)) {
                oLogger.info("process() : SessionCookie=not null");
                try {
                    ISSOService.logout(oLoginData.getSessionCookie());
                    oLogger.warn("process() : logoutException=false");
                } catch (IServiceException ise) {
                	oLogger.warn("process() : logoutException=true, Exception :"+ise.getMessage());
                    // IGNORE
                }
                oLoginData.setSessionCookie(IServiceUtil.generateUniqueID());
            }
            if (oLoginData.getMachineCookie() == null
                    || !verifyCookieState(oSPData.getGlobalInumber(),
                            oLoginData.getMachineCookie(), false)) {
                oLoginData.setMachineCookie(IServiceUtil.generateUniqueID());
            }
            // attempt login
            ISSOService.login(oSPData.getGlobalInumber(), oLoginForm
                    .getPassword(), oRequest.getServerName(), oLoginData
                    .getSessionCookie(), oLoginData.getMachineCookie(),
                    oLoginForm.isRememberMachine());

            oLogger.debug("process() : Artifact=" + sArtifact);
            String sArtifactDocXML = SAMLProcessor
                    .serializeArtifactDocument(sArtifact);
            // prepare positive saml authn. response
            String sAuthnResponse = SAMLProcessor
                    .serializePositiveAuthnLResponse(oAuthnRequestSAMLData
                            .getAssertionConsumerServiceURL(),
                            oAuthnRequestSAMLData.getID(), ISSOConstants
                                    .getProviderID(oRequest.getServerName()),
                            oSPData.getGlobalInumber(), oLoginData
                                    .getSessionCookie(), oAuthnRequestSAMLData
                                    .isReputationScoreRequired(),
                            oAuthnRequestSAMLData.getReputationScore(),
                            IServiceUtil.generateUniqueID(), IServiceUtil
                                    .generateUniqueID());
            // save response
            ISSOService.saveSAMLResponse(sArtifact, oLoginData
                    .getSessionCookie(), sAuthnResponse);
            oLogger.info("process() : Response is saved and Cookies is Set");
            // set the cookies
            setCookies(oResponse, oLoginData.getSessionCookie(), oLoginData
                    .getMachineCookie(), oLoginForm.isRememberMachine());
            // return artifact document back to SP's Assertion Consumer
            // Service
            String sReturnURL = null;
            try {
                sReturnURL = oAuthnRequestSAMLData
                        .getAssertionConsumerServiceURL()
                        + "?"
                        + ISSOClient.PARAM_SAML_ART
                        + "="
                        + URLEncoder.encode(sArtifactDocXML, "UTF-8")
                        + "&"
                        + ISSOClient.PARAM_RELAY_STATE
                        + "="
                        + URLEncoder.encode(oSPData.getRelayState(), "UTF-8");
                oLogger.info("process() : sReturnURL=" + sReturnURL);
            } catch (UnsupportedEncodingException ue) {
            	oLogger.warn("process() : Exception=" + ue.getMessage());
            }
            oActionForward = new ActionForward(sReturnURL, true);
        } catch (IServiceException oISExcep) {
            oLogger.warn("process() : Exception=" + oISExcep.getErrorCode());
            if (oLoginData.incrementLoginAttempts() > 2) {
                // generate negative authn. response after three attempts
                oLogger.debug("process() : Assertion=Negative");
                String sNegativeResponse = SAMLProcessor
                        .serializeNegativeAuthnResponse(oAuthnRequestSAMLData
                                .getAssertionConsumerServiceURL(),
                                oAuthnRequestSAMLData.getID(),
                                ISSOConstants.getProviderID(oRequest
                                        .getServerName()), IServiceUtil
                                        .generateUniqueID(),
                                SAMLProcessor.SAML_AUTH_FAIL_STATUS);
                String sArtifactDocXML = SAMLProcessor
                        .serializeArtifactDocument(sArtifact);
                oLogger.debug("process() : Artifact=" + sArtifact);                
                try {
                    ISSOService.saveSAMLResponse(sArtifact, null,
                            sNegativeResponse);
                    oLogger.info("process() : SAMLResponse is Saved" );
                } catch (IServiceException e) {
                    // go back to password screen
                    oMessages.add(ActionMessages.GLOBAL_MESSAGE,
                            new ActionMessage(ISSOConstants.ERR_INVALID_PWD));
                    saveMessages(oRequest, oMessages);
                    oLogger.warn("process() : Exception=" + e.getMessage());
                    oActionForward = oMapping.getInputForward();
                }
                try {
                    oActionForward = new ActionForward(oAuthnRequestSAMLData
                            .getAssertionConsumerServiceURL()
                            + "?"
                            + ISSOClient.PARAM_SAML_ART
                            + "="
                            + URLEncoder.encode(sArtifactDocXML, "UTF-8")
                            + "&"
                            + ISSOClient.PARAM_RELAY_STATE
                            + "="
                            + URLEncoder.encode(oSPData.getRelayState(),
                                    "UTF-8"), true);
                    oLogger.info("process() : SendRedirect=true");
                } catch (Exception e) {
                    // TODO ??
                    oLogger.warn("process() : Exception : URLCreation");
                }
            } else {
                // Forward to welcome page if already logged in
                if (oLoginData.isLoginStatus()) {
                    oLogger.info("process() : Return=Welcome Fwd");
                	oRequest
                            .setAttribute(ISSOConstants.REQ_PARAM_SPXRI, sSpXri);
                    return oMapping.findForward(ISSOConstants.WELCOME_FWD);
                }
                // Forward to login page
                if (oISExcep.getErrorCode() == 2028) {
                    oActionForward = oMapping
                            .findForward(ISSOConstants.INAME_FWD);
                    oLogger.error("process() : IservException=2028, Return=Iname Fwd");
                } else {
                    // go back to password screen
                    oMessages.add(ActionMessages.GLOBAL_MESSAGE,
                            new ActionMessage(ISSOConstants.ERR_INVALID_PWD));
                    saveErrors(oRequest, oMessages);
                    oActionForward = oMapping.getInputForward();
                }
            }
        }
        
        oRequest.setAttribute(ISSOConstants.REQ_PARAM_SPXRI, sSpXri);
        oRequest.setAttribute("SPData", oSPData);
        return oActionForward;
    }// process() ends

    private String getSamePageCookie(HttpServletRequest oRequest) {
        Cookie[] oCookie = oRequest.getCookies();
        if (oCookie != null) {
            for (int i = 0; i < oCookie.length; i++) {
                if (oCookie[i].getName().equals(SERV_ID_COOKIE_NAME)) {
                    return oCookie[i].getValue();
                }
            }
        }
        return null;
    }
}// Authenticate Action ends
